package com.linqi.freebie.ui.main

import com.linqi.freebie.base.BaseViewModel
import com.linqi.freebie.http.ktorClientCio.file.HttpFileHelper
import com.linqi.freebie.http.ktorClientCio.response.ResponseHolder
import com.linqi.freebie.repo.NetRepoImpl
import com.linqi.freebie.config.Constants
import com.linqi.freebie.config.Constants.REPO_LIST
import com.linqi.freebie.config.DepotInfo
import com.linqi.freebie.config.RepoInfo
import com.linqi.freebie.utils.CommandLineOutput
import com.linqi.freebie.utils.DepotParser
import com.linqi.freebie.utils.JsonUtils
import com.linqi.freebie.utils.JsonUtils.json
import com.linqi.freebie.utils.SteamPathFinder
import com.linqi.freebie.utils.SteamTools
import io.github.aakira.napier.Napier
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.jsonArray
import kotlinx.serialization.json.jsonObject
import java.io.File
import java.time.Instant
import java.time.LocalDateTime
import java.time.ZoneOffset
import java.time.format.DateTimeFormatter


/**
 *   1. 获取Github API请求限制
 *   2. 获取最新的仓库信息
 *   3. 获取仓库的commit信息
 */
class GithubViewModel: BaseViewModel() {

    // 延迟初始化，首次使用 userRepo 时才创建
    private val userRepo by lazy { NetRepoImpl() }

    var defaultAppId = "3489700"//剑星
    val depotMap = mutableMapOf<String, MutableList<String>>()
    val depotList = mutableListOf<DepotInfo>()

     fun loadData(appId: String=defaultAppId) {
         launchOnIO {
             if(checkRateLimit()){
                 doInit()

                 CommandLineOutput.info("开始执行任务...")

                 val repoInfo = getLatestRepoInfo(repoList=REPO_LIST,appId=appId)

                 if(repoInfo == null){
                     return@launchOnIO
                 }

                 Napier.i("repoInfo---------> ${JsonUtils.toJson(repoInfo)}")
                 val  branchUrl = "https://api.github.com/repos/${repoInfo?.name}/branches/$appId"
                 val branchRes = getStringResultByUrl(branchUrl)
                 Napier.i("branchRes---------> $branchRes")
                 // 解析 JSON
                 val jsonObj = json.parseToJsonElement(branchRes.toString()).jsonObject
                 val treeUrl = jsonObj["commit"]?.jsonObject["commit"]?.jsonObject["tree"]?.jsonObject["url"]?.toString()?.removeSurrounding("\"").toString()  // 移除字符串两边的引号
                 Napier.i("treeUrl---------> $treeUrl")
                 val treeRes = getStringResultByUrl(treeUrl)
                 Napier.i("treeRes---------> $treeRes")

                 val steamInstallPath = SteamPathFinder.getSteamPath()
                 if (steamInstallPath == null) {
                     CommandLineOutput.error("请先安装Steam...")
                     return@launchOnIO
                 }

                 val depotCache = File(steamInstallPath, "depotcache")
                 if (!depotCache.exists()) {
                     depotCache.mkdirs()
                 }

                 treeRes?.let {
                     val treeArray =Json.parseToJsonElement(treeRes).jsonObject["tree"]?.jsonArray
                     Napier.i("treeArray---------> $treeArray")
                     treeArray?.let {
                         for (item in treeArray) {
                             val filePath = item.jsonObject["path"]?.toString()?.removeSurrounding("\"").toString()  // 移除字符串两边的引号
                             if (filePath.endsWith(".manifest")) {
                                 val savePath = File(depotCache, filePath)
                                 if (savePath.exists()) {
                                     Napier.e("已存在清单: $savePath")
                                     CommandLineOutput.success("已存在清单...")
                                     continue
                                 }
                                 // 下载 manifest 文件
                                 val byteArrayContent  =  fetchFileContent(repoInfo?.name ?: "", repoInfo?.sha ?: "", filePath)
                                 byteArrayContent?.let{
                                     savePath.writeBytes(byteArrayContent)
                                     Napier.i("清单下载成功: $filePath")
                                     CommandLineOutput.success("清单下载成功...")
                                     val (depotId, manifestId) = DepotParser.parseManifestFilename(filePath)
                                     if (!depotId.isNullOrEmpty() && !manifestId.isNullOrEmpty()) {
                                         depotMap.getOrPut(depotId) { mutableListOf() }.add(manifestId)
                                     }


                                 }
                             } else if ("key.vdf" in filePath.lowercase()) {
                                 val byteArrayContent  =  fetchFileContent(repoInfo?.name ?: "", repoInfo?.sha ?: "", filePath)
                                 if (byteArrayContent != null) {
                                     depotList.addAll(DepotParser.parseKeyFile(byteArrayContent))
                                     Napier.i("key.vdf 解析成功: $filePath")
                                 } else {
                                     Napier.e("key.vdf 下载失败: $filePath")
                                 }
                             }
                         }
                     }
                 }

                 Napier.i("depotList: $depotList  depotMap:$depotMap")

                 if(depotList.isEmpty()){
                     Napier.e(message = "未找到此游戏的清单")
                     CommandLineOutput.success("未找到此游戏的清单...")
                     return@launchOnIO
                 }

                 val success=  SteamTools.setup(depotData=depotList,
                     depotMap = depotMap,
                     appId=appId,
                     steamPath=File(SteamPathFinder.getSteamPath().toString()),
                     versionLock = true)

                 if(success){
                     Napier.i(message = "游戏解锁配置成功！重启Steam后生效")
                     CommandLineOutput.info("游戏解锁配置成功！重启Steam后生效...")

                 }else{
                     Napier.i(message = "配置失败")
                 }

             }
         }

    }





    /**
     * 检查API请求限制
     */
    private suspend fun checkRateLimit(): Boolean {
        return try {
            val result = userRepo.checkRateLimit()
            when (result) {
                is ResponseHolder.Success -> {
                    result.data?.let { data ->
                        val json = Json.parseToJsonElement(data).jsonObject
                        val rate = json["rate"]?.jsonObject
                        val remaining = rate?.get("remaining")?.toString()?.toIntOrNull() ?: 0
                        val reset = rate?.get("reset")?.toString()?.toLongOrNull() ?: 0L
                        val resetTime = LocalDateTime.ofInstant(
                            Instant.ofEpochSecond(reset),
                            ZoneOffset.UTC
                        )

                        Napier.i("剩余Github API请求次数: $remaining")
                        CommandLineOutput.success("剩余Github API请求次数: $remaining")

                        if (remaining == 0) {
                            Napier.w("GitHub API 请求数已用尽，将在 $resetTime 重置")
                            CommandLineOutput.success("GitHub API 请求数已用尽，将在 $resetTime 重置")
                            false
                        } else {
                            true
                        }
                    } ?: false
                }
                is ResponseHolder.Failure -> {
                    Napier.e("GitHub API 请求失败: ${result.code} - ${result.message}")
                    CommandLineOutput.success("GitHub API 请求失败: ${result.code} - ${result.message}")

                    false
                }
                is ResponseHolder.Error -> {
                    Napier.e("GitHub API 请求错误: ${result.error}")
                    CommandLineOutput.success("GitHub API 请求错误: ${result.error}")
                    false
                }
            }
        } catch (e: Exception) {
            Napier.e("检查GitHub API限制时发生异常: ${e.message}")
            CommandLineOutput.success("检查GitHub API限制时发生异常: ${e.message}")
            false
        }
    }


    /**
     * 获取最新的仓库信息
     */
    private suspend fun getLatestRepoInfo(repoList: List<String>, appId: String): RepoInfo? {
        var latestDate: LocalDateTime? = null
        var selectedRepo: String? = null
        var selectedSha: String? = null

        for (repo in repoList) {
            val result = userRepo.getLatestRepoInfo(repo, appId)
            if (result is ResponseHolder.Success) {
                result.data?.let { data ->
                    val json = Json.parseToJsonElement(data).jsonObject
                    val commit = json["commit"]?.jsonObject
                    if (commit != null) {
                        val dateStr = commit["commit"]?.jsonObject
                            ?.get("author")?.jsonObject
                            ?.get("date")?.toString()
                            ?.replace("\"", "")

                        if (!dateStr.isNullOrBlank()) {
                            val date = LocalDateTime.parse(
                                dateStr.replace("Z", ""),
                                DateTimeFormatter.ISO_DATE_TIME
                            )
                            if (latestDate == null || date.isAfter(latestDate)) {
                                latestDate = date
                                selectedRepo = repo
                                selectedSha = commit["sha"]?.toString()?.replace("\"", "")
                            }
                        }
                    }
                }
            }
        }

        Napier.i("getLatestRepoInfo---------> latestDate:$latestDate selectedRepo: $selectedRepo selectedSha: $selectedSha")

        return if (latestDate != null && selectedRepo != null && selectedSha != null) {
            RepoInfo(
                name = selectedRepo,
                lastUpdate = latestDate,
                sha = selectedSha
            ).also {
                Napier.e("获取最新的仓库信息成功 -> repoInfo: $it")
                CommandLineOutput.success("获取最新的仓库信息成功 -> repoInfo: $it")
            }
        } else {
            Napier.e("无法找到有效的仓库信息 -> appid:$appId")
            CommandLineOutput.success("无法找到有效的仓库信息 -> appid:$appId.")
            null
        }
    }


    /**
     * getBranchRes
     */

    suspend fun getStringResultByUrl(url: String): String?{
        val result = userRepo.getStringResultByUrl(url)
        if (result is ResponseHolder.Success) {
            return result.data
        }
        return null
    }






    /**
     * 下载文件内容
     */
    private var isCN = true
    suspend fun fetchFileContent(repoName: String, sha: String, path: String): ByteArray? {
        val cdnList = if (isCN) Constants.CN_CDN_LIST else Constants.GLOBAL_CDN_LIST

        for (template in cdnList) {
            val url = template.replace("{repo}", repoName)
                .replace("{sha}", sha)
                .replace("{path}", path)



            try {
                Napier.e("存储路径: $path")
                val data = HttpFileHelper.downloadFileToByteArray(url = url) { downloadedBytes, totalBytes ->
                    totalBytes?.let {
                        if (it > 0) {
                            val percent = (downloadedBytes * 100 / it).toInt()
                            val downloadedKB = downloadedBytes / 1024
                            val totalKB = it / 1024
                            Napier.e("下载进度: $percent% ($downloadedKB KB / $totalKB KB)")
                        } else {
                            Napier.e("下载进度: $downloadedBytes bytes (未知总大小)")
                        }
                    }
                }

                if (data != null) {
                    Napier.e("下载完成: ${data.size} 字节")
                    CommandLineOutput.success("从 $url 下载完成: ${data.size} 字节")
                    return data // 成功下载就直接返回
                }
            } catch (e: Exception) {
                Napier.e("从 $url 下载失败: ${e.message}")
                CommandLineOutput.success("从 $url 下载失败: ${e.message}")
            }
        }
        CommandLineOutput.success("所有 CDN 尝试失败，未能下载文件: $path")
        Napier.e("所有 CDN 尝试失败，未能下载文件: $path")
        return null
    }




    private suspend fun doInit(){
        depotList.clear()
        depotMap.clear()
        CommandLineOutput.clear()
    }


}